#include "dsound.h"

#include "dsound_interfaces.h"

#include "..\common\def.h"
#include "..\common\util.h"
#include "..\engine\engine.h"

#include <dsound.h>
namespace EESound {

/**************************************
*
 **************************************/
CDSound9Device::CDSound9Device()
{
	ZeroMemory(&deviceCap ,sizeof(DSCAPS)); 
	lpDirectSound = NULL; 
}

/**************************************
 *TODO: might be useful to check if application
 *window is minimized, etc
 **************************************/
void CDSound9Device::Update(void)
{
}

/**************************************
 *When called this method will release all
 *resources it owns such as audio data. It
 *also shutsdown accelerated audio devices
 **************************************/
HRESULT CDSound9Device::Release(void)
{
	for(uint i = 0, n = m_SoundList.Count();  i < n;  i++)
	{
		IAudio* pSound = m_SoundList[i];
		if(FAILED(pSound->Release()))
			EE_LOGERROR("Failed to release sound.");

		EE_SAFEDELETE(pSound);
	}
	m_SoundList.Empty();
	
	if(FAILED(lpDirectSound->Release()))
		EE_LOGERROR("Failed to release DirectX audio interface.")
	lpDirectSound = NULL;
	CoUninitialize(); 
	
	return S_OK; 
}

/**************************************
 *Enables resources contained within the
 *device to be accessed
 **************************************/
HRESULT CDSound9Device::findResource(const string& indentity, IResource** pOut)
{
	HRESULT hr = S_OK;

	do
	{
		if(!indentity.IsValid()) {
			hr = E_FAIL;
			break;
		}

		string name = CUtil::getFilename(indentity); 
		if(!name.IsValid()) {
			hr = E_FAIL;
			break;
		}

		for(uint i = 0, n = m_SoundList.Count();  i < n;  i++) {
			
			IResource *pResource = m_SoundList[i];
			if(pResource) {
				if(name.equals(pResource->Identity()) == 0) {
					*pOut = pResource; 
					return hr; 
				}
			}
		}

	}while(false);

	*pOut = NULL; 
	return E_FAIL; 
}

/**************************************
 *Provides client access to functionality
 *for creating audio objects from valid data 
 *sources which can be manipulated during
 *runtime
 **************************************/
HRESULT CDSound9Device::createSound(const string& szFilename, IAudio** pOut, SoundType type)
{
	HRESULT hr = S_OK;
	IAudio *pAudioInst = NULL;
	string resourceIdentity;

	do {

		if(ISNULL(lpDirectSound)) {
			hr = E_FAIL;
			break;
		}

		if(!szFilename.IsValid()){
			hr = E_FAIL;
			break;
		}

		resourceIdentity = CUtil::getRemoveFilepath(szFilename);
		if(!resourceIdentity.IsValid()){
			hr = E_FAIL;
			break;
		}

		if(SUCCEEDED(findResource(resourceIdentity, (IResource**)pOut))){
			pAudioInst = (*pOut)->Clone();
			hr = S_OK;
			break;
		}

		switch(type) {
			case SoundType::MP3: 
				{
					pAudioInst = EE_NEW DevMP3(this);
				} break;
			case SoundType::WAV:
				{
					pAudioInst = EE_NEW DevWav(this);
				} break;
			case SoundType::AUTO:
			default:
				{
					string filenameFormat = CUtil::getFormat(szFilename);
					if(filenameFormat.equals("mp3", false))
						pAudioInst = EE_NEW DevMP3(this);
					else
						pAudioInst = EE_NEW DevWav(this);

				} break;
		}

		if(FAILED(pAudioInst->Create(szFilename))) {
			hr = E_FAIL;
			break;
		}

		m_SoundList.Add(pAudioInst);
		*pOut = pAudioInst;

		return S_OK;
	}while(false);

	//Error handling
	if(pAudioInst) {

		m_SoundList.Remove(pAudioInst);

		pAudioInst->Release();
		EE_SAFEDELETE(pAudioInst);
	}

	if(pOut) {
		*pOut = NULL;
	}
}

/**************************************
 *Fucntion that is responcibile for setting
 *up the audio device so that it is capable
 *of playing audio
 **************************************/
HRESULT CDSound9Device::Initailize()
{
	try{

		if(FAILED(CoInitialize(NULL)))
			throw EE_EXCEPTION("Failed to initialize COM"); 

		if(lpDirectSound)
			if(FAILED(lpDirectSound->Release()))
				throw EE_EXCEPTION( "Failed to Release old sound device"); 

		if(FAILED(CoCreateInstance(CLSID_DirectSound8,
			NULL, 
			CLSCTX_INPROC_SERVER,
			IID_IDirectSound8,
			(LPVOID*) &lpDirectSound)))
			throw EE_EXCEPTION("Failed to Create device"); 

		if(FAILED(lpDirectSound->Initialize(NULL)))
			throw EE_EXCEPTION("Failed to initalize device"); 

		if(FAILED(lpDirectSound->SetCooperativeLevel(CEngine::GetInstance()->GetWindow()->GetHWnd(), DSSCL_PRIORITY)))
			throw EE_EXCEPTION("Failed to set device cooperative level"); 


		deviceCap.dwSize = sizeof(DSCAPS);  
		if(FAILED(lpDirectSound->GetCaps(&deviceCap)))
			throw EE_EXCEPTION("Failed to get device capabilities"); 

		return S_OK; 

	}catch(CError::CErrorData error)
	{
		CError::CError(error, ERRORREPORT_LOG);
		return E_FAIL; 
	}
}

/**************************************
 *Utilitiy function that is provided
 *internally to enable sound buffer objects
 *to be duplicated via the internal audio 
 *acceleration device
 **************************************/
HRESULT CDSound9Device::cloneSoundBuffer(LPDIRECTSOUNDBUFFER pIn, LPDIRECTSOUNDBUFFER *pOut)
{
	HRESULT hr = S_OK;

	do {
		if(ISNULL(lpDirectSound)){
			hr = E_FAIL;
			break;
		}

		if(ISNULL(pIn) || ISNULL(pOut)){
			hr = E_INVALIDARG;
			break;
		}
		
        if(FAILED(lpDirectSound->DuplicateSoundBuffer(pIn, pOut))) {
			hr = E_FAIL;
			break;
		}

	}while(false);

	EE_LOGERROR("Failed to clone sound buffer.");
	return hr;
}

/**************************************
 *Utilitiy function that is provided
 *internally to enable sound buffer objects
 *to be created from the internal audio 
 *acceleration device
 **************************************/
HRESULT CDSound9Device::createSoundBuffer(LPDIRECTSOUNDBUFFER  *pInOut, WAVEFORMATEX* pFormat, DWORD length)
{ 
	HRESULT hr = S_OK;
	DSBUFFERDESC sbDesc;  
	LPDIRECTSOUNDBUFFER pSB = NULL;  

	do {
		if(ISNULL(lpDirectSound)){
			hr = E_FAIL;
			break;
		}

		if(length <= 0) {
			hr = E_INVALIDARG;
			break;
		}

		if(ISNULL(pFormat) || ISNULL(pInOut)){
			hr = E_INVALIDARG;
			break;
		}
		
		EE_ZEROMEMORY(&sbDesc, sizeof(sbDesc));  
		sbDesc.dwSize = sizeof(DSBUFFERDESC);  
		sbDesc.dwFlags = 
		DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;  
		sbDesc.dwBufferBytes =  length;
		sbDesc.lpwfxFormat = pFormat;

		if(FAILED(lpDirectSound->CreateSoundBuffer(&sbDesc, &pSB, NULL))){
			hr = E_FAIL;
			break;
		}
			
		if(FAILED(pSB->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) pInOut))){
			hr = E_FAIL;
			break;
		}
		return S_OK; 
	}while(false);

	//Error handling
	if(pInOut && *pInOut) {
		(*pInOut)->Release();
		*pInOut = NULL;
	}

	if(pSB) {
		pSB->Release();
		pSB = NULL;
	}
	return hr;
	
}
}